#ifndef LOGSPD_H
#define LOGSPD_H

#include "Core/platform.h"

#define GLM_ENABLE_EXPERIMENTAL
#include "glm/gtx/string_cast.hpp"

// #include <time.h>

// This ignores all warnings raised inside External headers
#pragma warning(push, 0)
#include <spdlog/spdlog.h>
#include <spdlog/fmt/ostr.h>
#pragma warning(pop)

namespace CC
{

	class CC_EXPORT Log
	{
	public:
        enum LogLevel
        {
            LogLevel_Trace = 0,
            LogLevel_Info,
            LogLevel_Warn,
            LogLevel_Error,
            LogLevel_Critical,
        };

		static void Init();
        static void LogOut(LogLevel level, const char* levelname, const char* format, ...);

        static void SetLogLevel(LogLevel level);
        static LogLevel GetLogLevel();

		// static std::shared_ptr<spdlog::logger> &GetCoreLogger() { return s_CoreLogger; }
		// static std::shared_ptr<spdlog::logger> &GetClientLogger() { return s_ClientLogger; }

	private:
		// static std::shared_ptr<spdlog::logger> s_CoreLogger;
		// static std::shared_ptr<spdlog::logger> s_ClientLogger;

        static LogLevel m_level;
	};

}

template <typename OStream, glm::length_t L, typename T, glm::qualifier Q>
inline OStream &operator<<(OStream &os, const glm::vec<L, T, Q> &vector)
{
	return os << glm::to_string(vector);
}

template <typename OStream, glm::length_t C, glm::length_t R, typename T, glm::qualifier Q>
inline OStream &operator<<(OStream &os, const glm::mat<C, R, T, Q> &matrix)
{
	return os << glm::to_string(matrix);
}

template <typename OStream, typename T, glm::qualifier Q>
inline OStream &operator<<(OStream &os, glm::qua<T, Q> quaternion)
{
	return os << glm::to_string(quaternion);
}

#define CC_DEBUG

// Core log macros
#define CC_CORE_TRACE(format, ...) CC::Log::LogOut(CC::Log::LogLevel_Trace, "CC_CORE_TRACE", format, __VA_ARGS__) //printf(__VA_ARGS__)	  //::CC::Log::GetCoreLogger()->trace(__VA_ARGS__)
#define CC_CORE_INFO(format, ...) CC::Log::LogOut(CC::Log::LogLevel_Info, "CC_CORE_INFO", format, __VA_ARGS__) //printf(__VA_ARGS__)	  //::CC::Log::GetCoreLogger()->info(__VA_ARGS__)
#define CC_CORE_WARN(format, ...) CC::Log::LogOut(CC::Log::LogLevel_Warn, "CC_CORE_WARN", format, __VA_ARGS__) //printf(__VA_ARGS__)	  //::CC::Log::GetCoreLogger()->warn(__VA_ARGS__)
#define CC_CORE_ERROR(format, ...) CC::Log::LogOut(CC::Log::LogLevel_Error, "CC_CORE_ERROR", format, __VA_ARGS__) //printf(__VA_ARGS__)	  //::CC::Log::GetCoreLogger()->error(__VA_ARGS__)
#define CC_CORE_CRITICAL(format, ...) CC::Log::LogOut(CC::Log::LogLevel_Critical, "CC_CORE_CRITICAL", format, __VA_ARGS__) //printf(__VA_ARGS__) //::CC::Log::GetCoreLogger()->critical(__VA_ARGS__)

// Client log macros
#define CC_TRACE(format, ...) CC::Log::LogOut(CC::Log::LogLevel_Trace, "CC_TRACE", format, __VA_ARGS__) //printf(__VA_ARGS__) //::CC::Log::GetClientLogger()->trace(__VA_ARGS__)
#define CC_INFO(format, ...)  CC::Log::LogOut(CC::Log::LogLevel_Info, "CC_INFO", format, __VA_ARGS__) //printf(__VA_ARGS__)	 //::CC::Log::GetClientLogger()->info(__VA_ARGS__)
#define CC_WARN(format, ...) CC::Log::LogOut(CC::Log::LogLevel_Warn, "CC_WARN", format, __VA_ARGS__) //printf(__VA_ARGS__)	 //::CC::Log::GetClientLogger()->warn(__VA_ARGS__)
#define CC_ERROR(format, ...) CC::Log::LogOut(CC::Log::LogLevel_Error, "CC_ERROR", format, __VA_ARGS__) //printf(__VA_ARGS__)	 //::CC::Log::GetClientLogger()->error(__VA_ARGS__)
#define CC_CRITICAL(format, ...) CC::Log::LogOut(CC::Log::LogLevel_Critical, "CC_CRITICAL", format, __VA_ARGS__) //printf(__VA_ARGS__) //::CC::Log::GetClientLogger()->critical(__VA_ARGS__)

#define CC_ASSERT(result, format, ...)      \
	if (!(result))                    \
	{                               \
		CC_CORE_ERROR(format, __VA_ARGS__); \
		exit(0);                    \
	}

#endif